#include "XmlHeaders.h"
#include "DtdHeaders.h"
#include <sys/types.h>
#include <regex.h>

FILE * f_error; /* to write a log of all errors */
int nb_errors;

/** Check that element e is valid according to the dtd
    @return true if the xml is valid according to the dtd, else false
**/
bool checkElement(const ElementXML * e_xml, const DTD * dtd);

/** Check if attributes of element e are valid according to the DTD
**/
bool checkAttributes(const ElementXML * e_xml, const ElementDTD* e_dtd);

/** Check if the expression corresponding to XML match the regular expression of the DTD **/
bool matchRegExp(const string regExpDTD, const string expXML);

/** Add the message string in the file f_error and increment nb_errors**/
void addError( const string error );



/** This function check if the document XML xml is valid according to the DTD dtd
     @return true if it is valid, false if it is not
    All the errors (if there are) will be written in the file f_error
 **/
bool check_conformity(const XML * xml, const DTD * dtd)
{
    nb_errors = 0;
    f_error = fopen("log_errors.txt", "w+");

    checkElement( xml->getRoot(), dtd ); /* call the recursive function checkElement with the root of xml document */

    return nb_errors? false : true;
}


bool checkElement(const ElementXML * e_xml, const DTD * dtd)
{
    /* first we check if e_xml is defined in the dtd */
    string element_name = e_xml->getName();
    ElementDTD * e_dtd = dtd->getElementByName( element_name );
    if( e_dtd == NULL )
    {
        string error = "Element " + element_name + " does not exist in DTD";
        addError(error);
        //return false;
    }

    else
    {
        /* if e_xml exists in the DTD, then we check the validity of each of its attribute */

        /*if( ! checkAttributes(e_xml, e_dtd)
            return false;*/
        checkAttributes(e_xml, e_dtd);


        /* then we check if the children of e_xml match the regular expression corresponding to the DTD */
        bool resRegExp = matchRegExp(e_dtd->getRegExp(), e_xml->getRegExpChildren() );

        if( ! resRegExp )
        {
	    cout<<e_xml->getRegExpChildren()<< " does not match "<<e_dtd->getRegExp() << endl;
            string error = "Children of element " + element_name + " do not match the regular expression of the DTD";
            addError(error);
        }

    }

    /* then we check recursively the validity of each son */
    vector<NodeXML*> * children = e_xml->getChildren();
    vector<NodeXML*>::const_iterator it;

    for( it = children->begin() ; it != children->end() ; ++it)
    {
        if ( ! ( (*it)->getRegExp() == "<PCDATA>" ) )
	{
		checkElement( (ElementXML*) *it, dtd );
	}
    }
}


bool checkAttributes(const ElementXML * e_xml, const ElementDTD* e_dtd)
{
    bool ret = true;
    vector<AttributeXML*> * attributes = e_xml->getAttributes();
    vector<AttributeXML*>::iterator it;

    for(it = attributes->begin() ; it != attributes->end() ; ++it)
    {
        /* cheking if the attribute is defined in the DTD */
        if ( e_dtd->getAttributeByName( (*it)->getName()  ) == NULL )
        {
            string error = "Attribute " + (*it)->getName() + " does not exist for element " + e_dtd->getName() + " in the DTD";
            addError(error);
            ret = false;
            //return false;
        }
    }

    return ret;
}

bool matchRegExp(const string regExpDTD, const string expXML)
{
        regex_t preg;
        regcomp(&preg, regExpDTD.c_str(), REG_EXTENDED | REG_NOSUB);
        int match = regexec(&preg, expXML.c_str(), 0, 0, 0);

        if ( match == 0 ) /* string does not match regular expression */
            return true;

        return false;
}

void addError( const string error )
{
    //fwrite(error.c_str(), 1, sizeof(error), f_error);
    fprintf(f_error, "%s\n", error.c_str());
    nb_errors++;
}


//bool isChildren(const ElementDTD * father, const ElementXML * child)
//{
//    //check if father has children and if child is one of them
//    vector<ElementDTD*> children = father->getChildren();
//    if ( !children.empty() )
//    {
//        vector<ElementDTD*>::iterator it;
//        for (it = children.begin(); it != children.end(); it++)
//            if ((*it)->getName()  == child->getName())
//                return true;
//    }
//
//    return false
//
//}
